#ifndef db_ANY_H
#define db_ANY_H

#include <boost/any.hpp>
#include <ostream>
#include <istream>
#include <utility/Time.h>
#include <utility/String.h>
#include <utility/Streams.h>

namespace serika
{
	namespace db
	{
		class Any : public boost::any
		{
		public:
			template<typename T>
			const T& Get(bool throwOnError = false) const;
		
			template<typename T>
			bool TryGet(T& t)  const;
	
			bool IsEmpty() const 
			{
				return boost::any::empty();
			}
	
			bool IsNil() const
			{
				return this->IsEmpty();
			}
	
			void Clear()
			{
				any a;
				any::swap(a);
			}
		public:
			Any() {}
	
			Any(const Any& value)
				: boost::any(static_cast<const boost::any&>(value)) {}
	
			template<typename ValueType>
			Any(const ValueType& value)
				: boost::any(value) {}
	
			template<typename ValueType>
			Any& operator=(const ValueType & rhs)
			{
				any::operator =(rhs);
				return *this;
			}
	
			Any& operator=(const Any & rhs)
			{
				any::operator = (static_cast<const boost::any&>(rhs));
				return *this;
			}
	
			template<typename ValueType>
			bool Contains() const
			{
				return this->type() == typeid(ValueType);
			}
	
			ut::String ToString() const;
			ut::String ToPersistence() const;
			ut::Ostream& ToPersistence(ut::Ostream& os) const;
			bool FromPersistence(const ut::String& text, size_t& start);
			bool FromPersistence(ut::Istream& is);
	
			size_t ToBinary(ut::Char* buffer, size_t bufferLen) const;
			size_t FromBinary(const ut::Char* data, size_t dataLen);
		private:
			size_t ReadValueFromStream(ut::Istream& is, ut::Char* buf, size_t length);
		};
	
		static Any NIL; 
	
		template<typename T>
		inline bool Any::TryGet( T& t ) const
		{
			if (IsEmpty() || typeid(T) != this->type())
			{
				return false;
			}
	
			try
			{
				t = boost::any_cast<T>(*this);
			}
			catch(boost::bad_any_cast&)
			{
				return false;
			}
			return true;
		}
	
		template<>
		inline bool Any::TryGet(int& t) const
		{
			if (!this->IsEmpty() && (this->type() == typeid(int) || this->type() == typeid(unsigned int)
				|| this->type() == typeid(short) || this->type() == typeid(unsigned short)))
			{
				t = *boost::unsafe_any_cast<int>(this);
				return true;
			}
			else 
			{
				return false;
			}
		}
	
		template<>
		inline bool Any::TryGet(unsigned int& t) const
		{
			if (!this->IsEmpty() && (this->type() == typeid(int) || this->type() == typeid(unsigned int)
				|| this->type() == typeid(short) || this->type() == typeid(unsigned short)))
			{
				t = *boost::unsafe_any_cast<unsigned int>(this);
				return true;
			}
			else 
			{
				return false;
			}
		}
	
		template<>
		inline bool Any::TryGet(ut::String& s) const
		{
			if (this->IsEmpty())
			{
				return false;
			}
	
			if (this->type() == typeid(ut::String))
			{
				s = *boost::unsafe_any_cast<ut::String>(this);
				return true;
			}
			else if (this->type() == typeid(ut::Time))
			{
				s = this->Get<ut::Time>().ToString();
				return true;
			}
			else
			{
				return false;
			}
		}
	
		template<>
		inline bool Any::TryGet(time_t& t) const
		{
			if (this->IsEmpty())
			{
				return false;
			}
	
			if (this->type() == typeid(time_t))
			{
				t = *boost::unsafe_any_cast<time_t>(this);
				return true;
			}
			else if (this->type() == typeid(ut::Time))
			{
				t = this->Get<ut::Time>().ToTimeT();
				return true;
			}
			else
			{
				return false;
			}
		}
		
		template<typename T> T DefaultValue()
		{
			return T();
		}
	
		template<> static ut::String DefaultValue()
		{
			return ut::String();
		}

		template<> static int DefaultValue()
		{
			return 0;
		}
	
		template<> static ut::Time DefaultValue<ut::Time>()
		{
			return ut::Time::INVALID;
		}
	
		template<typename T>
		inline const T& Any::Get(bool throwOnError) const
		{
			static T dv = DefaultValue<T>();
			const T* v = 0;
			if (IsEmpty())
			{
				return dv;
			}
			if ((v = boost::any_cast<T>(this)) != 0)
			{
				return *v;
			}
			
			if ((v = boost::unsafe_any_cast<T>(this)) != 0)
			{
				return *v;
			}
			if (throwOnError)
			{
				throw boost::bad_any_cast();
			}
			return dv;
		}
	
		template<>
		inline const int& Any::Get(bool throwOnError) const
		{
			static int i = 0;
			if (!this->IsEmpty() && (this->type() == typeid(int) || this->type() == typeid(unsigned int)
				|| this->type() == typeid(short) || this->type() == typeid(unsigned short)))
			{
				return *boost::unsafe_any_cast<int>(this);
			}
			else 
			{
				if (throwOnError)
				{
					throw boost::bad_any_cast();
				}
				return i;
			}
		}
	
		template<>
		inline const unsigned int& Any::Get(bool throwOnError) const
		{
			static unsigned int i = 0;
			if (!this->IsEmpty() && (this->type() == typeid(int) || this->type() == typeid(unsigned int)
				|| this->type() == typeid(short) || this->type() == typeid(unsigned short)))
			{
				return *boost::unsafe_any_cast<unsigned int>(this);
			}
			else 
			{
				if (throwOnError)
				{
					throw boost::bad_any_cast();
				}
				return i;
			}
		}
	
		template<>
		inline const ut::String& Any::Get(bool throwOnError) const
		{
			static ut::String dv = _T("");
			if (this->IsEmpty())
			{
				return dv;
			}
	
			if (this->type() == typeid(ut::String))
			{
				return *boost::unsafe_any_cast<ut::String>(this);
			}
			else if (this->type() == typeid(ut::Time))
			{
				static ut::String sv = this->Get<ut::Time>().ToString();
				return sv;
			}
			else
			{
				if (throwOnError)
				{
					throw boost::bad_any_cast();
				}
				return dv;
			}
		}
	
		template<>
		inline const time_t& Any::Get(bool throwOnError) const
		{
			static time_t dv = 0;
			if (this->IsEmpty())
			{
				return dv;
			}
	
			if (this->type() == typeid(time_t))
			{
				return *boost::unsafe_any_cast<time_t>(this);
			}
			else if (this->type() == typeid(ut::Time))
			{
				static time_t tv = this->Get<ut::Time>().ToTimeT();
				return tv;
			}
			else
			{
				if (throwOnError)
				{
					throw boost::bad_any_cast();
				}
				return dv;
			}
		}
	
		template<typename ValueType1, typename ValueType2>
		bool operator == (const Any& operand1, const Any& operand2)
		{
			ValueType1 v1;
			ValueType2 v2;
	
			if (operand1.IsNil() && operand2.IsNil())
			{
				return true;
			}
			else if (operand1.IsNil() || operand2.IsNil())
			{
				return false;
			}
			
			if (!operand1.TryGet(v1) || !operand2.TryGet(v2))
			{
				return false;
			}
			return v1 == v2;
		}
	}	
	
	//ut::Ostream& operator << (ut::Ostream& s, const db::Any& v);
	std::ostream& operator << (std::ostream& s, const db::Any& v);
	std::wostream& operator << (std::wostream& s, const db::Any& v);
}

#endif
